本篇文章主要介绍 Spring IoC 容器怎么加载 bean
的定义元信息。
前言 本篇文章主要介绍 Spring IoC 容器怎么加载 bean
的定义元信息。
下图是一个大致的流程图:
第一次画图,画的有点烂。😂
正文 首先定义一个简单的 POJO,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class User { private Long id; private String name; public Long getId () { return id; } public void setId (Long id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } @Override public String toString () { return "User{" + "id=" + id + ", name='" + name + '\'' + '}' ; } }
再编写一个 XML 文件。
1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="user" class ="com.leisurexi.ioc.domain.User" > <property name ="id" value ="1" /> <property name ="name" value ="leisurexi" /> </bean > </beans >
最后再来一个测试类。
1 2 3 4 5 6 7 8 @Test public void test () { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions("META-INF/spring-bean.xml" ); User user = beanFactory.getBean("user" , User.class); System.out.println(user); }
上面这段代码比较简单,无非就是声明 bean
工厂,然后通过指定的 XML 文件加载 bean
的定义元信息,最后通过 bean
工厂获取 bean
。
DefaultListableBeanFactory 首先我们来了解一下 DefaultListableBeanFactory
,下面是该类的类图及层次结构。
AliasRegistry: 定义对 alias
的简单增删改等操作。
SimpleAliasRegistry: 主要使用 map
作为 alias
的缓存,并对接口 AliasRegistry
进行实现。
SingletonBeanRegistry: 定义了对单例 bean 的注册及获取。
BeanFactory: 定义获取单个 bean
及 bean
的各种属性。
DefaultSingletonBeanRegistry: 对接口 SingletonBeanRegistry
各函数的实现。
HierarchicalBeanFactory: 继承 BeanFactory
,也就是在 BeanFactory
定义的功能的基础上增加了对 parentBeanFactory
的支持。
BeanDefinitionRegistry: 定义了对 BeanDefinition
的各种增删改操作。
FactoryBeanRegistrySupport: 在 DefaultSingletonBeanRegistry
基础上增加了对 FactoryBean
的特殊处理功能。
ConfigurableBeanFactory: 提供配置 BeanFactory
的各种方法。
ListableBeanFactory: 继承 BeanFactory
提供了获取多个 bean
的各种方法。
AbstractBeanFactory: 综合 FactoryBeanRegistrySupport
和 ConfigurableBeanFactory
的功能。
AutowireCapableBeanFactory: 提供创建 bean
、自动注入、初始化以及应用 bean
的后处理器。
AbstractAutowireCapableBeanFactory: 综合 AbstractBeanFactory
并对接口 AutowireCapableBeanFactory
进行实现。
ConfigurableListableBeanFactory: BeanFactory
配置清单,指定忽略类型及接口等。
DefaultListableBeanFactory: 综合上面所有功能,主要是对 bean
注册后的处理。
可以看到上面的接口大多数是定义了一些功能或在父接口上扩展了一些功能,DefaultListableBeanFactory
实现了所有接口,大多数默认情况下我们所使用的 beanFactory
就是 DefaultListableBeanFactory
。
AbstractBeanDefinitionReader#loadBeanDefinitions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public int loadBeanDefinitions (String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null ) { throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available" ); } if (resourceLoader instanceof ResourcePatternResolver) { try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int count = loadBeanDefinitions(resources); if (actualResources != null ) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]" ); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]" , ex); } } else { Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null ) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]" ); } return count; } }
上面方法主要是将资源文件转换为 Resource
对象,然后调用 loadBeanDefinitions(Resource...)
加载 BeanDefinition
。
1 2 3 4 5 6 7 8 9 public int loadBeanDefinitions (Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null" ); int count = 0 ; for (Resource resource : resources) { count += loadBeanDefinitions(resource); } return count; }
该方法主要就是遍历 resources
然后调用 XmlBeanDefinitionReader#loadBeanDefinitions(Resource)
。
XmlBeanDefinitionReader#loadBeanDefinitions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public int loadBeanDefinitions (Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions (EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null" ); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } Set<EncodedResource> currentResources = this .resourcesCurrentlyBeingLoaded.get(); if (currentResources == null ) { currentResources = new HashSet<>(4 ); this .resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!" ); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null ) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this .resourcesCurrentlyBeingLoaded.remove(); } } }
上面主要将 Resource
封装成 EncodedResource
,也就是制定资源的编码和字符集。然后获取 Resource
的输入流 InputStream
,并封装成 InputSource
设置其编码,最终调用 doLoadBeanDefinitions
开始真正的加载流程。
XmlBeanDefinitionReader#doLoadBeanDefinitions 1 2 3 4 5 6 7 8 9 10 11 12 13 protected int doLoadBeanDefinitions (InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } }
上面代码抛开异常处理,逻辑非常简单,就是用 inputSource
和 resource
加载 XML 文件,并封装成 Document
对象,然后去注册 BeanDefinition
。
XmlBeanDefinitionReader#doLoadDocument 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 protected Document doLoadDocument (InputSource inputSource, Resource resource) throws Exception { return this .documentLoader.loadDocument(inputSource, getEntityResolver(), this .errorHandler,getValidationModeForResource(resource), isNamespaceAware()); } protected int getValidationModeForResource (Resource resource) { int validationModeToUse = getValidationMode(); if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } int detectedMode = detectValidationMode(resource); if (detectedMode != VALIDATION_AUTO) { return detectedMode; } return VALIDATION_XSD; } public Document loadDocument (InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isTraceEnabled()) { logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]" ); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); }
detectValidationMode()
方法其实就是读取文件内容,判断是否包含 DOCTYPE
,如果包含就是 DTD 否则就是 XSD。
获取 XML 配置文件的验证模式。XML 文件的验证模式是用来保证 XML 文件的正确性,常见的验证模式有 DTD 和 XSD。
DTD XML 格式示例:
STD XML 格式示例:
XmlBeanDefinitionReader#registerBeanDefinitions 1 2 3 4 5 6 7 8 9 10 public int registerBeanDefinitions (Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
这里的 getRegistry()
方法返回的就是 DefaultListableBeanFactory
,因为就只有它实现了 BeanDefinitionRegistry
接口。
DefaultListableBeanFactory
中定义了存放 BeanDefinition
的缓存,所以 getBeanDefinitionCount()
方法返回的就是 beanDefinitionMap
的数量。
1 2 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256 );
DefaultBeanDefinitionDoucumentReader#registerBeanDefinitions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public void registerBeanDefinitions (Document doc, XmlReaderContext readerContext) { this .readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); } protected void doRegisterBeanDefinitions (Element root) { BeanDefinitionParserDelegate parent = this .delegate; this .delegate = createDelegate(getReaderContext(), root, parent); if (this .delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource()); } return ; } } } preProcessXml(root); parseBeanDefinitions(root, this .delegate); postProcessXml(root); this .delegate = parent; }
profile
主要是用于多环境开发,例如:
集成到 Web 环境时,在 web.xml 中加入以下代码:
1 2 3 4 <coontext-param > <param-name > Spring.profiles.active</param-name > <param-value > dev</param-value > </coontext-param >
preProcessXml()
和 postProcessXml()
采用的 模板方法模式 ,子类可以继承 DefaultBeanDefinitionDoucumentReader
来重写这两个方法,这也是解析前后的扩展点。
DefaultBeanDefinitionDoucumentReader#parseBeanDefinitions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 protected void parseBeanDefinitions (Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } private void parseDefaultElement (Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { doRegisterBeanDefinitions(ele); } }
上面 parseDefaultElement
方法中对 bean 标签的处理方法 processBeanDefinition
最为重要,下面来着重分析一下。
DefaultBeanDefinitionDocumentReader#processBeanDefinition 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 protected void processBeanDefinition (Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null ) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'" , ele, ex); } getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
上面代码主要步骤如下:
将 Element
解析成 BeanDefinitionHolder
。
若存在默认标签下的子节点下有自定义属性,需要再次对自定义标签再进行解析。
注册 BeanDefinition
。
发出响应事件,通知相关监听器,这个 bean 已经注册完,具体详情可以查看 ReaderEventListener#componentRegistered()
方法。可以通过以下方式去注册这个监听器:
BeanDefinitionParseDelegate#parseBeanDefinitionElement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 public BeanDefinitionHolder parseBeanDefinitionElement (Element ele) { return parseBeanDefinitionElement(ele, null ); } public BeanDefinitionHolder parseBeanDefinitionElement (Element ele, @Nullable BeanDefinition containingBean) { String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0 ); if (logger.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases" ); } } if (containingBean == null ) { checkNameUniqueness(beanName, aliases, ele); } AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null ) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null ) { beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this .readerContext.getRegistry(), true ); } else { beanName = this .readerContext.generateBeanName(beanDefinition); String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this .readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]" ); } } catch (Exception ex) { error(ex.getMessage(), ele); return null ; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null ; } public AbstractBeanDefinition parseBeanDefinitionElement (Element ele, String beanName, @Nullable BeanDefinition containingBean) { this .parseState.push(new BeanEntry(beanName)); String className = null ; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null ; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { AbstractBeanDefinition bd = createBeanDefinition(className, parent); parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); parseMetaElements(ele, bd); parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); parseConstructorArgElements(ele, bd); parsePropertyElements(ele, bd); parseQualifierElements(ele, bd); bd.setResource(this .readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found" , ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found" , ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing" , ele, ex); } finally { this .parseState.pop(); } return null ; }
上面代码主要将 bean
标签,解析为 BeanDefinitionHolder
返回,主要步骤如下:
解析 id
、name
属性,将 name
按照 ,
或者 ;
分割作为别名 (alias
)。
解析剩下的属性,并封装成 GenericBeanDefinition
。
调用 parseBeanDefinitionAttributes
方法解析 bean
标签的所有属性。
调用 parseMetaElements
方法解析元数据信息。
调用 parseLookupOverrideSubElements
方法解析 lookup-method
子标签。
调用 parseReplacedMethodSubElements
方法解析 replaced-method
子标签。
调用 parseConstructorArgElements
方法解析 constructor-arg
子标签。
调用 parsePropertyElements
方法解析 property
子标签。
调用 parseQualifierElements
方法解析 qualifier
子标签。
判断 beanName
是否存在,不存在会根据 Spring 的命名规则生成一个。
使用 beanDefinition
、beanName
、aliasesArray
构建 BeanDefinitionHolder
返回。
我们这边可以简单看一下 BeanDefinitionHolder
的属性,如下:
1 2 3 4 5 6 7 8 9 10 11 12 public class BeanDefinitionHolder implements BeanMetadataElement { private final BeanDefinition beanDefinition; private final String beanName; @Nullable private final String[] aliases; ...省略其它代码 }
BeanDefinitionHolder
其实就是对 BeanDefinition
的包装。
parseBeanDefinitionAttributes 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 public AbstractBeanDefinition parseBeanDefinitionAttributes (Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration" , ele); } else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } else if (containingBean != null ) { bd.setScope(containingBean.getScope()); } if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (isDefaultValue(lazyInit)) { lazyInit = this .defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if (isDefaultValue(autowireCandidate)) { String candidatePattern = this .defaults.getAutowireCandidates(); if (candidatePattern != null ) { String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); } else if (this .defaults.getInitMethod() != null ) { bd.setInitMethodName(this .defaults.getInitMethod()); bd.setEnforceInitMethod(false ); } if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); } else if (this .defaults.getDestroyMethod() != null ) { bd.setDestroyMethodName(this .defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false ); } if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
上面方法完成了对所有 bean
标签属性的解析。值得注意的地方是如果同时指定了 bean
标签的 init-method
和 beans
标签的 default-init-method
属性,那么优先使用前者,destory-mehtod
标签也是一样。
大家可以去看一下 AbstractBeanDefinition
中定义的属性就一目了然了,这里限于篇幅原因就不展示了。
这里先回顾一下元数据 meta
属性的使用。
这个属性并不会体现在 user
的属性当中,而是一个额外的声明,当需要使用里面的信息时可以通过 BeanDefinition#getAttribute(key)
来获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void parseMetaElements (Element ele, BeanMetadataAttributeAccessor attributeAccessor) { NodeList nl = ele.getChildNodes(); for (int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) { Element metaElement = (Element) node; String key = metaElement.getAttribute(KEY_ATTRIBUTE); String value = metaElement.getAttribute(VALUE_ATTRIBUTE); BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value); attribute.setSource(extractSource(metaElement)); attributeAccessor.addMetadataAttribute(attribute); } } }
parseConstructorArgElements 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public void parseConstructorArgElements (Element beanEle, BeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for (int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { parseConstructorArgElement((Element) node, bd); } } } public void parseConstructorArgElement (Element ele, BeanDefinition bd) { String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(indexAttr)) { try { int index = Integer.parseInt(indexAttr); if (index < 0 ) { error("'index' cannot be lower than 0" , ele); } else { try { this .parseState.push(new ConstructorArgumentEntry(index)); Object value = parsePropertyValue(ele, bd, null ); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele); } else { bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); } } finally { this .parseState.pop(); } } } catch (NumberFormatException ex) { error("Attribute 'index' of tag 'constructor-arg' must be an integer" , ele); } } else { try { this .parseState.push(new ConstructorArgumentEntry()); Object value = parsePropertyValue(ele, bd, null ); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); } finally { this .parseState.pop(); } } }
上面代码首先提取 constructor-arg
标签中必要的属性 (index
、type
、name
)。
如果指定了 index
属性:
解析 constructor-arg
的子元素。
使用 ConstructorArgumentsValues.ValueHolder
类型来封装解析出来的元素。
将 type
、name
和 index
属性一并封装在 ConstructorArgumentsValues.ValueHolder
类型中,并添加到当前 BeanDefinition
的 ConstructorArgumentValues
中的 LinkedHashMap
类型的属性indexedArgumentValues
中。
如果有指定 index
属性:
解析 constructor-arg
的子元素。
使用 ConstructorArgumentsValues.ValueHolder
类型来封装解析出来的元素。
将 type
、name
和 index
属性一并封装在 ConstructorArgumentsValues.ValueHolder
类型中,并添加到当前 BeanDefinition
的 ConstructorArgumentValues
中的 ArrayList
类型的属性genericArgumentValues
中。
parsePropertyValue 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 public Object parsePropertyValue (Element ele, BeanDefinition bd, @Nullable String propertyName) { String elementName = (propertyName != null ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element" ); NodeList nl = ele.getChildNodes(); Element subElement = null ; for (int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) { if (subElement != null ) { error(elementName + " must not contain more than one sub-element" , ele); } else { subElement = (Element) node; } } } boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null )) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element" , ele); } if (hasRefAttribute) { String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute" , ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } else if (hasValueAttribute) { TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } else if (subElement != null ) { return parsePropertySubElement(subElement, bd); } else { error(elementName + " must specify a ref or value" , ele); return null ; } } public Object parsePropertySubElement (Element ele, @Nullable BeanDefinition bd) { return parsePropertySubElement(ele, bd, null ); } public Object parsePropertySubElement (Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) { if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null ) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } else if (nodeNameEquals(ele, REF_ELEMENT)) { String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false ; if (!StringUtils.hasLength(refName)) { refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true ; if (!StringUtils.hasLength(refName)) { error("'bean' or 'parent' is required for <ref> element" , ele); return null ; } } if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute" , ele); return null ; } RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); ref.setSource(extractSource(ele)); return ref; } else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } else if (nodeNameEquals(ele, NULL_ELEMENT)) { TypedStringValue nullHolder = new TypedStringValue(null ); nullHolder.setSource(extractSource(ele)); return nullHolder; } else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } else { error("Unknown property sub-element: [" + ele.getNodeName() + "]" , ele); return null ; } }
从上面的代码来看,对构造函数中属性元素的解析,步骤如下:
略过 description
或者 meta
。
提取 constructor-arg
上的 ref
和 value
属性,以便于根据规则验证正确性。其规则为在 constructor-arg
上不存在一下情况:
同时存在 ref
和 value
属性。
存在 ref
或者 value
属性,并且又有子元素。
parsePropertyElements 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public void parsePropertyElements (Element beanEle, BeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for (int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { parsePropertyElement((Element) node, bd); } } } public void parsePropertyElement (Element ele, BeanDefinition bd) { String propertyName = ele.getAttribute(NAME_ATTRIBUTE); if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute" , ele); return ; } this .parseState.push(new PropertyEntry(propertyName)); try { if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'" , ele); return ; } Object val = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this .parseState.pop(); } }
上面方法主要是遍历 property
节点,然后解析属性值封装成 PropertyValue
添加到 BeanDefinition
的 PropertyValues
中。
注意:property
节点类似于 POJO 中的 set
方法,bean
中的属性必需有 set
方法否则会抛出异常。
parseQualifierElements 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 public void parseQualifierElements (Element beanEle, AbstractBeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for (int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) { parseQualifierElement((Element) node, bd); } } } public void parseQualifierElement (Element ele, AbstractBeanDefinition bd) { String typeName = ele.getAttribute(TYPE_ATTRIBUTE); if (!StringUtils.hasLength(typeName)) { error("Tag 'qualifier' must have a 'type' attribute" , ele); return ; } this .parseState.push(new QualifierEntry(typeName)); try { AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName); qualifier.setSource(extractSource(ele)); String value = ele.getAttribute(VALUE_ATTRIBUTE); if (StringUtils.hasLength(value)) { qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value); } NodeList nl = ele.getChildNodes(); for (int i = 0 ; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) { Element attributeEle = (Element) node; String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE); String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE); if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) { BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue); attribute.setSource(extractSource(attributeEle)); qualifier.addMetadataAttribute(attribute); } else { error("Qualifier 'attribute' tag must have a 'name' and 'value'" , attributeEle); return ; } } } bd.addQualifier(qualifier); } finally { this .parseState.pop(); } }
对于 qualifier
元素的获取,我们大多数接触的更多是注解的形式,在使用 Spring 框架中进行自动注入时,Spring 容器中匹配的候选 Bean
必需有且只有一个。如果存在多个类型相同的 Bean
,且按照类型注入时,Spirng 允许通过 Qualifier
指定注入 Bean
的名称,这样歧义就消除了。
BeanDefinitionReaderUtils#registerBeanDefinition 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 public static void registerBeanDefinition (BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); String[] aliases = definitionHolder.getAliases(); if (aliases != null ) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } public void registerBeanDefinition (String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty" ); Assert.notNull(beanDefinition, "BeanDefinition must not be null" ); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed" , ex); } } BeanDefinition existingDefinition = this .beanDefinitionMap.get(beanName); if (existingDefinition != null ) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]" ); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]" ); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]" ); } } this .beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { synchronized (this .beanDefinitionMap) { this .beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this .beanDefinitionNames.size() + 1 ); updatedDefinitions.addAll(this .beanDefinitionNames); updatedDefinitions.add(beanName); this .beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else { this .beanDefinitionMap.put(beanName, beanDefinition); this .beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this .frozenBeanDefinitionNames = null ; } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } } public void registerAlias (String name, String alias) { Assert.hasText(name, "'name' must not be empty" ); Assert.hasText(alias, "'alias' must not be empty" ); synchronized (this .aliasMap) { if (alias.equals(name)) { this .aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name" ); } } else { String registeredName = this .aliasMap.get(alias); if (registeredName != null ) { if (registeredName.equals(name)) { return ; } if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'." ); } if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'" ); } } checkForAliasCircle(name, alias); this .aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'" ); } } } }
上面代码有两个变量比较重要 beanDefinitionMap
和 beanDefinitionNames
,下面代码是这两个属性在 DefaultListableBeanFactory
中的定义:
1 2 3 4 5 6 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256 );private volatile List<String> beanDefinitionNames = new ArrayList<>(256 );
如果 BeanDefinition
是 AbstractBeanDefinition
类型,验证 Bean
的格式是否正确。
这次效验主要是对于 AbstractBeanDefinition
属性中的 methodOverrides
的校验,校验 methodOverrides
是否与 工厂方法 并存或者 methodOverrides
中的方法根本不存在。
判断该 beanName
的 BeanDefinition
是否已经注册过;如果存在判断是否允许覆盖,允许的话直接替换,不允许直接抛出异常。
默认的情况下是允许的,但是在 Spring Boot 2.1 开始这里会手动的设置为不允许。
beanName
对应的 BeanDefinition
以前没有注册过,判断 bean
是否已经开始创建;如果在创建中对 beanDefinitionMap
进行加锁 (这里虽然 beanDefinitionMap
是线程安全的 ConcurrentHashMap
,单个操作是线程安全的但多个操作不是,所以这里手动加锁),然后将 beanName
和 BeanDefinition
以 key-value
形式放入 beanDefinitionMap
缓存中,然后写时复制一份 beanDefinitionNames
,将 beaName
缓存进去,记录 bean
的注册顺序;如果不在创建直接将 BeanDefinition
和 beanName
分别放入 beanDefinitionMap
和 beanDefinitionNames
中。
最后判断如果 BeanDefinition
已经注册过,或者 beanName
已经存在单例对象,则将该 beanName
对应的缓存信息、单例对象清除,因为这些对象都是由老的 BeanDefinition
创建的,需要被覆盖掉。再用新的 BeanDefinition
来创建这些缓存和单例对象。
总结 本文主要介绍了通过 XML 文件的方式注册 Bean
,我们可以重新梳理一下思路:
解析 XML 文件,构建成 AbstractBeanDefinition (GenericBeanDefinition)
对象来存放所有解析出来的属性。
将 AbstractBeanDefinition
、beanName
、aliasesArray
构建成 BeanDefinitionHolder
对象。
最后通过 BeanDefinitionHolder
将 beanName
和 BeanDefinition
注册到 DefaultListableBeanFactory
中,也就是保存起来。
上文提到的两个比较重要的属性 beanDefinitionNames
和 beanDefinitionMap
,在后面都会多次用到,可以重点关注一下。
最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址:https://github.com/leisurexi/tiny-spring 。访问新博客地址,观看效果更佳 https://leisurexi.github.io/
参考